<template>
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        :width="width"
        :close-on-click-modal="closeOnClickModal"
        :close-on-press-escape="closeOnPressEscape"
        :show-close="showClose"
        :draggable="draggable"
        :align-center="alignCenter"
        :destroy-on-close="destroyOnClose"
        @open="handleOpen"
        @opened="handleOpened"
        @close="handleClose"
        @closed="handleClosed"
    >
        <!-- 默认插槽 - 内容区域 -->
        <slot></slot>
        
        <!-- 具名插槽 - 页头 -->
        <template #header="{ close, titleId, titleClass }">
            <slot name="header" :close="close" :titleId="titleId" :titleClass="titleClass">
                <div class="my-header">
                    <span :id="titleId" :class="titleClass">{{ title }}</span>
                    <el-button v-if="showClose" class="el-dialog__headerbtn" @click="close">
                        <el-icon class="el-dialog__close"><Close /></el-icon>
                    </el-button>
                </div>
            </slot>
        </template>

        <!-- 具名插槽 - 页脚 -->
        <template #footer>
            <slot name="footer">
                <div class="dialog-footer">
                    <el-button @click="handleCancel" v-if="showCancelButton">{{ cancelButtonText }}</el-button>
                    <el-button type="primary" @click="handleConfirm" v-if="showConfirmButton">
                        {{ confirmButtonText }}
                    </el-button>
                </div>
            </slot>
        </template>
    </el-dialog>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { Close } from '@element-plus/icons-vue'

interface Props {
    // 控制弹窗显示/隐藏
    modelValue: boolean
    // 标题
    title?: string
    // 宽度
    width?: string | number
    // 是否显示关闭按钮
    showClose?: boolean
    // 是否显示取消按钮
    showCancelButton?: boolean
    // 是否显示确认按钮
    showConfirmButton?: boolean
    // 取消按钮文字
    cancelButtonText?: string
    // 确认按钮文字
    confirmButtonText?: string
    // 是否可以通过点击 modal 关闭 Dialog
    closeOnClickModal?: boolean
    // 是否可以通过按下 ESC 关闭 Dialog
    closeOnPressEscape?: boolean
    // 是否可拖拽
    draggable?: boolean
    // 是否对头部和底部采用居中布局
    alignCenter?: boolean
    // 关闭时销毁其中的元素
    destroyOnClose?: boolean
    // 打开前的回调
    beforeOpen?: () => Promise<boolean> | boolean
    // 关闭前的回调
    beforeClose?: () => Promise<boolean> | boolean
}

// 定义组件属性
const props = withDefaults(defineProps<Props>(), {
    modelValue: false,
    title: '标题',
    width: '50%',
    showClose: true,
    showCancelButton: true,
    showConfirmButton: true,
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    closeOnClickModal: true,
    closeOnPressEscape: true,
    draggable: false,
    alignCenter: false,
    destroyOnClose: false
})

// 定义事件
const emit = defineEmits<{
    (e: 'update:modelValue', value: boolean): void
    (e: 'open'): void
    (e: 'opened'): void
    (e: 'close'): void
    (e: 'closed'): void
    (e: 'cancel'): void
    (e: 'confirm'): void
}>()

// 内部控制弹窗显示/隐藏的变量
const dialogVisible = ref(props.modelValue)

// 监听外部传入的modelValue变化
watch(() => props.modelValue, (newVal) => {
    dialogVisible.value = newVal
})

// 监听内部dialogVisible变化
watch(() => dialogVisible.value, (newVal) => {
    emit('update:modelValue', newVal)
})

// 打开弹窗的处理函数
const handleOpen = async () => {
    if (props.beforeOpen) {
        const canOpen = await props.beforeOpen()
        if (!canOpen) {
            dialogVisible.value = false
            return
        }
    }
    emit('open')
}

// 弹窗打开动画结束时的处理函数
const handleOpened = () => {
    emit('opened')
}

// 关闭弹窗的处理函数
const handleClose = async () => {
    if (props.beforeClose) {
        const canClose = await props.beforeClose()
        if (!canClose) {
            dialogVisible.value = true
            return
        }
    }
    emit('close')
}

// 弹窗关闭动画结束时的处理函数
const handleClosed = () => {
    emit('closed')
}

// 取消按钮点击处理函数
const handleCancel = () => {
    dialogVisible.value = false
    emit('cancel')
}

// 确认按钮点击处理函数
const handleConfirm = () => {
    dialogVisible.value = false
    emit('confirm')
}
</script>

<style scoped>
.dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
}

.my-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
</style>
